package com.zeyu.framework.core.web.exception;

import com.google.common.collect.Maps;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.Map;

/**
 * 异常处理器。该类会处理所有在执行标有@RequestMapping注解的方法时发生的异常
 * <p>
 * 通用的异常处理逻辑，即ResponseEntityExceptionHandler类，
 * 该类的handleException()方法上标注了@ExceptionHandler注解。
 * 此方法会判断 Exception 的类型，对于每一种类型，会交由对应的protected handleXXXX()方法处理，子类只需要重写对应的方法即可
 */
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    // ================================================================
    // Constants
    // ================================================================

    // ================================================================
    // Fields
    // ================================================================

    // ================================================================
    // Constructors
    // ================================================================

    // ================================================================
    // Methods from/for super Interfaces or SuperClass
    // ================================================================

    /**
     * 处理@RequestParam错误, 即参数不足
     */
    @Override
    @ResponseBody
    protected ResponseEntity<Object> handleMissingServletRequestParameter(
            MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String, Object> info = Maps.newHashMap();
        info.put("error", "参数错误");
        return new ResponseEntity<>(info, status);
    }

    /**
     * 处理500错误,不是所有的,只是spring监控的十几种,其余交给 ErrorController 统一处理
     * Provides handling for standard Spring MVC exceptions.
     */
    @Override
    @ResponseBody
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers,
                                                             HttpStatus status, WebRequest request) {
        logger.error("got internal error : {}", ex);

        Map<String, Object> info = Maps.newHashMap();

        // 请求方式不支持
        if (ex instanceof HttpRequestMethodNotSupportedException) {
            info.put("error", "请求方式不支持");
            return new ResponseEntity<>(info, status);
        }
        info.put("error", "系统异常");
        return new ResponseEntity<>(info, status);
    }


    /**
     * 处理参数类型转换失败
     */
    @Override
    @ResponseBody
    protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers,
                                                        HttpStatus status, WebRequest request) {
        logger.error("type mismatch");
        Map<String, Object> info = Maps.newHashMap();
        info.put("error", "处理参数类型转换失败");
        return new ResponseEntity<>(info, status);
    }

    // ================================================================
    // Public or Protected Methods
    // ================================================================

    @ExceptionHandler({UnauthorizedException.class, UnauthenticatedException.class})
    @ResponseBody
    public ResponseEntity<Object> handleUnauthorizedException(Exception ex, WebRequest request) {
        HttpStatus status = HttpStatus.FORBIDDEN;

        HttpHeaders headers = new HttpHeaders();
        request.getHeaderNames().forEachRemaining(name -> headers.add(name, request.getHeader(name)));

        logger.debug("auth error: {}", ex);

        Map<String, Object> info = Maps.newHashMap();
        info.put("error", "请求未授权,请用授权用户登陆");
        return new ResponseEntity<>(info, headers, status);
    }

    // ================================================================
    // Getter & Setter
    // ================================================================

    // ================================================================
    // Private Methods
    // ================================================================

    // ================================================================
    // Inner or Anonymous Class
    // ================================================================

    // ================================================================
    // Test Methods
    // ================================================================

}
